/* Author: Mark Faust;   
 * Description: This file defines the two required functions for the branch predictor.
*/

/****************************************************************************
  ECE 486 Project 2
  Branch Prediction

  Jinho Park
  Albert Pham
  Antoni Romano
  Hoa Quach

  predictor.cc

  The ALPHA21264 tournament predictor is implementation in the following
functions

    bool PREDICTOR::get_prediction
    void PREDICTOR::update_predictor
****************************************************************************/


#include "predictor.h"
#include <iostream>
using namespace std;

int LocalHistory[ LOCAL_SIZE ] = {0};		//Local History Table
int LocalPrediction[ LOCAL_SIZE ] = {0};	//Local Predictor Table
int GlobalPrediction[ GLOBAL_SIZE ] = {0};	//Global Predictor Table
int GlobalChoice[ GLOBAL_SIZE ] = {0};		//Global Choice Predictor Table

int BranchHistory = 0;				//Branch History Shift Register
unsigned int PC = 0;				//Program Counter bits


    bool PREDICTOR::get_prediction( const branch_record_c* br, const op_state_c* os )
        {
			int localH;
			int localP;
			int GlobalC;
			int GlobalP;

			PC = br->instruction_addr;
			PC = PC & PC_MASK;		//select 10 bit for use in branch predictor
			PC = PC >> 2;
			
			GlobalC = GlobalChoice[BranchHistory];
			
			if ((GlobalC & GLOBAL_MASK) == 0x02) //if thepredictor choice is 1 -> use the global predictor
			{
				
				GlobalP = GlobalPrediction[BranchHistory];

				if ((GlobalP & GLOBAL_MASK) == 0x02)
					return TAKEN;
				else
					return NOT_TAKEN;
			}
			
			else				//else ethe
			{
				localH = LocalHistory[PC];
				localP = LocalPrediction[localH];

				if ((localP & LOCAL_MASK) == 0x4)
					return TAKEN;
				else
					return NOT_TAKEN;

			}

			
        }
		
		
    // Update the predictor after a prediction has been made.  This should accept
    // the branch record (br) and architectural state (os), as well as a third
    // argument (taken) indicating whether or not the branch was taken.
    void PREDICTOR::update_predictor(const branch_record_c* br, const op_state_c* os, bool taken)
        {
			int LocalH;
			int LocalP;
			int GlobalC;
			int GlobalP;
			
			bool LP;
			bool GP;

			LocalH = LocalHistory[ PC ];
			LocalP = LocalPrediction[ LocalH ];

			GlobalC = GlobalChoice[ BranchHistory ];
			GlobalP = GlobalPrediction[ BranchHistory ];
		
			// Convert the local and global predictions to boolean values.
			if( LocalP & LP_MSB_A_1 )
				LP = TAKEN;
			else
				LP = NOT_TAKEN;
				
			if( GlobalP & GP_MSB_A_1 )
				GP = TAKEN;
			else
				GP = NOT_TAKEN;
		
			// If the branch was actually taken, then increment the local and global prediction counters.
			// Otherwise, decrement the local and global prediction counters.
			if( taken )
			{
				switch( LocalP )
				{
					case LT_STRONGLY: 		break;	// Predictor bits saturated.
					
					case LT_LESS_STRONGLY:	LocalPrediction[ LocalH ] = LT_STRONGLY;
											break;
					
					case LT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LT_LESS_STRONGLY;
											break;
					
					case LT_WEAKLY: 		LocalPrediction[ LocalH ] = LT_LESS_WEAKLY;
											break;
					
					case LNT_WEAKLY: 		LocalPrediction[ LocalH ] = LT_WEAKLY;
											break;
					
					case LNT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LNT_WEAKLY;
											break;
					
					case LNT_LESS_STRONGLY: LocalPrediction[ LocalH ] = LNT_LESS_WEAKLY;
											break;
					
					case LNT_STRONGLY: 		LocalPrediction[ LocalH ] = LNT_LESS_STRONGLY;
											break;
					
					default: 				cerr << "Predictor Bits out of Range!!!" << endl;
											break;
				}

				switch( GlobalP )
				{
					case GT_STRONGLY: 	break;	// Predictor bits saturated.	
					
					case GT_WEAKLY:		GlobalPrediction[ BranchHistory ] = GT_STRONGLY;
										break;
					
					case GNT_WEAKLY: 	GlobalPrediction[ BranchHistory ] = GT_WEAKLY;
										break;
					
					case GNT_STRONGLY:	GlobalPrediction[ BranchHistory ] = GNT_WEAKLY;
										break;
					
					default: 			cerr << "Predictor Bits out of Range!!!" << endl;
										break;
				}
			}
			else	// The branch was not taken.
			{
				switch( LocalP )
				{
					case LT_STRONGLY: 		LocalPrediction[ LocalH ] = LT_LESS_STRONGLY;
											break;
					
					case LT_LESS_STRONGLY: 	LocalPrediction[ LocalH ] = LT_LESS_WEAKLY;
											break;
					
					case LT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LT_WEAKLY;
											break;
					
					case LT_WEAKLY: 		LocalPrediction[ LocalH ] = LNT_WEAKLY;
											break;
					
					case LNT_WEAKLY: 		LocalPrediction[ LocalH ] = LNT_LESS_WEAKLY;
											break;
					
					case LNT_LESS_WEAKLY: 	LocalPrediction[ LocalH ] = LNT_LESS_STRONGLY;
											break;
					
					case LNT_LESS_STRONGLY: LocalPrediction[ LocalH ] = LNT_STRONGLY;
											break;
					
					case LNT_STRONGLY: 		break;	// Predictor bits saturated.
					
					default: 				cerr << "Predictor Bits out of Range!!!" << endl;
											break;
				}

				switch( GlobalP )
				{
					case GT_STRONGLY: 	GlobalPrediction[ BranchHistory ] = GT_WEAKLY;
										break;	
					
					case GT_WEAKLY:		GlobalPrediction[ BranchHistory ] = GNT_WEAKLY;
										break;
					
					case GNT_WEAKLY: 	GlobalPrediction[ BranchHistory ] = GNT_STRONGLY;
										break;
					
					case GNT_STRONGLY: 	break;	// Predictor bits saturated.
					
					default: 			cerr << "Predictor Bits out of Range!!!" << endl;
										break;
				}
			}

			// If the local and global predictions are different, then train the global choice
			// to prefer the correct prediction.  If the local and global predictions are the
			// same, no learning is done.
			if( GP != LP )
			{
				if( GP == taken )
				{
					switch( GlobalC )
					{
						case GT_STRONGLY:	break;	// Predictor bits saturated.
						
						case GT_WEAKLY:		GlobalChoice[ BranchHistory ] = GT_STRONGLY;
											break;

						case GNT_WEAKLY: 	GlobalChoice[ BranchHistory ] = GT_WEAKLY;
											break;
						
						case GNT_STRONGLY: 	GlobalChoice[ BranchHistory ] = GNT_WEAKLY;
											break;
						
						default: 			cerr << "Predictor Bits out of Range!!!" << endl;
											break;
					}
				}
				else // LP == taken
				{
					switch( GlobalC )
					{
						case GT_STRONGLY: 	GlobalChoice[ BranchHistory ] = GT_WEAKLY;
											break;	
						
						case GT_WEAKLY: 	GlobalChoice[ BranchHistory ] = GNT_WEAKLY;
											break;
						
						case GNT_WEAKLY: 	GlobalChoice[ BranchHistory ] = GNT_STRONGLY;
											break;
						
						case GNT_STRONGLY: 	break;
						
						default: 			cerr << "Predictor Bits out of Range!!!" << endl;
											break;
					}
				}
			}

			// Update the branch history with the actual outcome of the branch.
			if( taken )
			{
				BranchHistory = BranchHistory << 1; 
				BranchHistory = BranchHistory & BRANCH_MASK;
				BranchHistory = BranchHistory | BRANCH_TAKEN;
			}
			else // Branch not taken.
			{
				BranchHistory = BranchHistory << 1; 
				BranchHistory = BranchHistory & BRANCH_MASK;
			}
			
			// Update the local history with the actual outcome of the branch.
			if( taken )
			{
				LocalHistory[ PC ] = LocalHistory[ PC ] << 1;
				LocalHistory[ PC ] = LocalHistory[ PC ] & LOCAL_HISTORY_MASK;
				LocalHistory[ PC ] = LocalHistory[ PC ] | BRANCH_TAKEN;
			}
			else // Branch not taken.
			{
				LocalHistory[ PC ] = LocalHistory[ PC ] << 1;
				LocalHistory[ PC ] = LocalHistory[ PC ] & LOCAL_HISTORY_MASK;
			}

        }
